1/* $NetBSD: postmulti.c,v 1.2 2017/02/14 01:16:47 christos Exp $ */
2
3/*++
4/* NAME
5/* postmulti 1
6/* SUMMARY
7/* Postfix multi-instance manager
8/* SYNOPSIS
9/* .fi
10/* .ti -4
11/* \fBEnabling multi-instance management:\fR
12/*
13/* \fBpostmulti\fR \fB-e init\fR [\fB-v\fR]
14/*
15/* .ti -4
16/* \fBIterator mode:\fR
17/*
18/* \fBpostmulti\fR \fB-l\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
19/* [\fB-i \fIname\fR]
20/*
21/* \fBpostmulti\fR \fB-p\fR [\fB-av\fR] [\fB-g \fIgroup\fR]
22/* [\fB-i \fIname\fR] \fIcommand...\fR
23/*
24/* \fBpostmulti\fR \fB-x\fR [\fB-aRv\fR] [\fB-g \fIgroup\fR]
25/* [\fB-i \fIname\fR] \fIcommand...\fR
26/*
27/* .ti -4
28/* \fBLife-cycle management:\fR
29/*
30/* \fBpostmulti\fR \fB-e create\fR [\fB-av\fR]
31/* [\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR]
32/* [\fB-I \fIname\fR] [\fIparam=value\fR ...]
33/*
34/* \fBpostmulti\fR \fB-e import\fR [\fB-av\fR]
35/* [\fB-g \fIgroup\fR] [\fB-i \fIname\fR] [\fB-G \fIgroup\fR]
36/* [\fB-I \fIname\fR] [\fBconfig_directory=\fI/path\fR]
37/*
38/* \fBpostmulti\fR \fB-e destroy\fR [\fB-v\fR] \fB-i \fIname\fR
39/*
40/* \fBpostmulti\fR \fB-e deport\fR [\fB-v\fR] \fB-i \fIname\fR
41/*
42/* \fBpostmulti\fR \fB-e enable\fR [\fB-v\fR] \fB-i \fIname\fR
43/*
44/* \fBpostmulti\fR \fB-e disable\fR [\fB-v\fR] \fB-i \fIname\fR
45/*
46/* \fBpostmulti\fR \fB-e assign\fR [\fB-v\fR] \fB-i \fIname\fR
47/* [\fB-I \fIname\fR] [-G \fIgroup\fR]
48/* DESCRIPTION
49/* The \fBpostmulti\fR(1) command allows a Postfix administrator
50/* to manage multiple Postfix instances on a single host.
51/*
52/* \fBpostmulti\fR(1) implements two fundamental modes of
53/* operation. In \fBiterator\fR mode, it executes the same
54/* command for multiple Postfix instances. In \fBlife-cycle
55/* management\fR mode, it adds or deletes one instance, or
56/* changes the multi-instance status of one instance.
57/*
58/* Each mode of operation has its own command syntax. For this
59/* reason, each mode is documented in separate sections below.
60/* BACKGROUND
61/* .ad
62/* .fi
63/* A multi-instance configuration consists of one primary
64/* Postfix instance, and one or more secondary instances whose
65/* configuration directory pathnames are recorded in the primary
66/* instance's main.cf file. Postfix instances share program
67/* files and documentation, but have their own configuration,
68/* queue and data directories.
69/*
70/* Currently, only the default Postfix instance can be used
71/* as primary instance in a multi-instance configuration. The
72/* \fBpostmulti\fR(1) command does not currently support a \fB-c\fR
73/* option to select an alternative primary instance, and exits
74/* with a fatal error if the \fBMAIL_CONFIG\fR environment
75/* variable is set to a non-default configuration directory.
76/*
77/* See the MULTI_INSTANCE_README tutorial for a more detailed
78/* discussion of multi-instance management with \fBpostmulti\fR(1).
79/* ITERATOR MODE
80/* .ad
81/* .fi
82/* In iterator mode, \fBpostmulti\fR performs the same operation
83/* on all Postfix instances in turn.
84/*
85/* If multi-instance support is not enabled, the requested
86/* command is performed just for the primary instance.
87/* .PP
88/* Iterator mode implements the following command options:
89/* .SH "Instance selection"
90/* .IP \fB-a\fR
91/* Perform the operation on all instances. This is the default.
92/* .IP "\fB-g \fIgroup\fR"
93/* Perform the operation only for members of the named \fIgroup\fR.
94/* .IP "\fB-i \fIname\fR"
95/* Perform the operation only for the instance with the specified
96/* \fIname\fR. You can specify either the instance name
97/* or the absolute pathname of the instance's configuration
98/* directory. Specify "-" to select the primary Postfix instance.
99/* .IP \fB-R\fR
100/* Reverse the iteration order. This may be appropriate when
101/* updating a multi-instance system, where "sink" instances
102/* are started before "source" instances.
103/* .sp
104/* This option cannot be used with \fB-p\fR.
105/* .SH "List mode"
106/* .IP \fB-l\fR
107/* List Postfix instances with their instance name, instance
108/* group name, enable/disable status and configuration directory.
109/* .SH "Postfix-wrapper mode"
110/* .IP \fB-p\fR
111/* Invoke \fBpostfix(1)\fR to execute the specified \fIcommand\fR.
112/* This option implements the \fBpostfix-wrapper\fR(5) interface.
113/* .RS
114/* .IP \(bu
115/* With "start"-like commands, "postfix check" is executed for
116/* instances that are not enabled. The full list of commands
117/* is specified with the postmulti_start_commands parameter.
118/* .IP \(bu
119/* With "stop"-like commands, the iteration order is reversed,
120/* and disabled instances are skipped. The full list of commands
121/* is specified with the postmulti_stop_commands parameter.
122/* .IP \(bu
123/* With "reload" and other commands that require a started
124/* instance, disabled instances are skipped. The full list of
125/* commands is specified with the postmulti_control_commands
126/* parameter.
127/* .IP \(bu
128/* With "status" and other commands that don't require a started
129/* instance, the command is executed for all instances.
130/* .RE
131/* .IP
132/* The \fB-p\fR option can also be used interactively to
133/* start/stop/etc. a named instance or instance group. For
134/* example, to start just the instances in the group "msa",
135/* invoke \fBpostmulti\fR(1) as follows:
136/* .RS
137/* .IP
138/* # postmulti -g msa -p start
139/* .RE
140/* .SH "Command mode"
141/* .IP \fB-x\fR
142/* Execute the specified \fIcommand\fR for all Postfix instances.
143/* The command runs with appropriate environment settings for
144/* MAIL_CONFIG, command_directory, daemon_directory,
145/* config_directory, queue_directory, data_directory,
146/* multi_instance_name, multi_instance_group and
147/* multi_instance_enable.
148/* .SH "Other options"
149/* .IP \fB-v\fR
150/* Enable verbose logging for debugging purposes. Multiple
151/* \fB-v\fR options make the software increasingly verbose.
152/* LIFE-CYCLE MANAGEMENT MODE
153/* .ad
154/* .fi
155/* With the \fB-e\fR option \fBpostmulti\fR(1) can be used to
156/* add or delete a Postfix instance, and to manage the
157/* multi-instance status of an existing instance.
158/* .PP
159/* The following options are implemented:
160/* .SH "Existing instance selection"
161/* .IP \fB-a\fR
162/* When creating or importing an instance, place the new
163/* instance at the front of the secondary instance list.
164/* .IP "\fB-g \fIgroup\fR"
165/* When creating or importing an instance, place the new
166/* instance before the first secondary instance that is a
167/* member of the specified group.
168/* .IP "\fB-i \fIname\fR"
169/* When creating or importing an instance, place the new
170/* instance before the matching secondary instance.
171/* .sp
172/* With other life-cycle operations, apply the operation to
173/* the named existing instance. Specify "-" to select the
174/* primary Postfix instance.
175/* .SH "New or existing instance name assignment"
176/* .IP "\fB-I \fIname\fR"
177/* Assign the specified instance \fIname\fR to an existing
178/* instance, newly-created instance, or imported instance.
179/* Instance
180/* names other than "-" (which makes the instance "nameless")
181/* must start with "postfix-". This restriction reduces the
182/* likelihood of name collisions with system files.
183/* .IP "\fB-G \fIgroup\fR"
184/* Assign the specified \fIgroup\fR name to an existing instance
185/* or to a newly created or imported instance.
186/* .SH "Instance creation/deletion/status change"
187/* .IP "\fB-e \fIaction\fR"
188/* "Edit" managed instances. The following actions are supported:
189/* .RS
190/* .IP \fBinit\fR
191/* This command is required before \fBpostmulti\fR(1) can be
192/* used to manage Postfix instances. The "postmulti -e init"
193/* command updates the primary instance's main.cf file by
194/* setting:
195/* .RS
196/* .IP
197/* .nf
198/* multi_instance_wrapper =
199/* ${command_directory}/postmulti -p --
200/* multi_instance_enable = yes
201/* .fi
202/* .RE
203/* .IP
204/* You can set these by other means if you prefer.
205/* .IP \fBcreate\fR
206/* Create a new Postfix instance and add it to the
207/* multi_instance_directories parameter of the primary instance.
208/* The "\fB-I \fIname\fR" option is recommended to give the
209/* instance a short name that is used to construct default
210/* values for the private directories of the new instance. The
211/* "\fB-G \fIgroup\fR" option may be specified to assign the
212/* instance to a group, otherwise, the new instance is not a
213/* member of any groups.
214/* .sp
215/* The new instance main.cf is the stock main.cf with the
216/* parameters that specify the locations of shared files cloned
217/* from the primary instance. For "nameless" instances, you
218/* should manually adjust "syslog_name" to yield a unique
219/* "logtag" starting with "postfix-" that will uniquely identify
220/* the instance in the mail logs. It is simpler to assign the
221/* instance a short name with the "\fB-I \fIname\fR" option.
222/* .sp
223/* Optional "name=value" arguments specify the instance
224/* config_directory, queue_directory and data_directory.
225/* For example:
226/* .RS
227/* .IP
228/* .nf
229/* # postmulti -I postfix-mumble \e
230/* -G mygroup -e create \e
231/* config_directory=/my/config/dir \e
232/* queue_directory=/my/queue/dir \e
233/* data_directory=/my/data/dir
234/* .fi
235/* .RE
236/* .IP
237/* If any of these pathnames is not supplied, the program
238/* attempts to generate the pathname by taking the corresponding
239/* primary instance pathname, and by replacing the last pathname
240/* component by the value of the \fB-I\fR option.
241/* .sp
242/* If the instance configuration directory already exists, and
243/* contains both a main.cf and master.cf file, \fBcreate\fR
244/* will "import" the instance as-is. For existing instances,
245/* \fBcreate\fR and \fBimport\fR are identical.
246/* .IP \fBimport\fR
247/* Import an existing instance into the list of instances
248/* managed by the \fBpostmulti\fR(1) multi-instance manager.
249/* This adds the instance to the multi_instance_directories
250/* list of the primary instance. If the "\fB-I \fIname\fR"
251/* option is provided it specifies the new name for the instance
252/* and is used to define a default location for the instance
253/* configuration directory (as with \fBcreate\fR above). The
254/* "\fB-G \fIgroup\fR" option may be used to assign the instance
255/* to a group. Add a "\fBconfig_directory=\fI/path\fR" argument
256/* to override a default pathname based on "\fB-I \fIname\fR".
257/* .IP \fBdestroy\fR
258/* Destroy a secondary Postfix instance. To be a candidate for
259/* destruction an instance must be disabled, stopped and its
260/* queue must not contain any messages. Attempts to destroy
261/* the primary Postfix instance trigger a fatal error, without
262/* destroying the instance.
263/* .sp
264/* The instance is removed from the primary instance main.cf
265/* file's alternate_config_directories parameter and its data,
266/* queue and configuration directories are cleaned of files
267/* and directories created by the Postfix system. The main.cf
268/* and master.cf files are removed from the configuration
269/* directory even if they have been modified since initial
270/* creation. Finally, the instance is "deported" from the list
271/* of managed instances.
272/* .sp
273/* If other files are present in instance private directories,
274/* the directories may not be fully removed, a warning is
275/* logged to alert the administrator. It is expected that an
276/* instance built using "fresh" directories via the \fBcreate\fR
277/* action will be fully removed by the \fBdestroy\fR action
278/* (if first disabled). If the instance configuration and queue
279/* directories are populated with additional files (access and
280/* rewriting tables, chroot jail content, etc.) the instance
281/* directories will not be fully removed.
282/* .sp
283/* The \fBdestroy\fR action triggers potentially dangerous
284/* file removal operations. Make sure the instance's data,
285/* queue and configuration directories are set correctly and
286/* do not contain any valuable files.
287/* .IP \fBdeport\fR
288/* Deport a secondary instance from the list of managed
289/* instances. This deletes the instance configuration directory
290/* from the primary instance's multi_instance_directories list,
291/* but does not remove any files or directories.
292/* .IP \fBassign\fR
293/* Assign a new instance name or a new group name to the
294/* selected instance. Use "\fB-G -\fR" to specify "no group"
295/* and "\fB-I -\fR" to specify "no name". If you choose to
296/* make an instance "nameless", set a suitable syslog_name in
297/* the corresponding main.cf file.
298/* .IP \fBenable\fR
299/* Mark the selected instance as enabled. This just sets the
300/* multi_instance_enable parameter to "yes" in the instance's
301/* main.cf file.
302/* .IP \fBdisable\fR
303/* Mark the selected instance as disabled. This means that
304/* the instance will not be started etc. with "postfix start",
305/* "postmulti -p start" and so on. The instance can still be
306/* started etc. with "postfix -c config-directory start".
307/* .SH "Other options"
308/* .IP \fB-v\fR
309/* Enable verbose logging for debugging purposes. Multiple
310/* \fB-v\fR options make the software increasingly verbose.
311/* .RE
312/* ENVIRONMENT
313/* .ad
314/* .fi
315/* The \fBpostmulti\fR(1) command exports the following environment
316/* variables before executing the requested \fIcommand\fR for a given
317/* instance:
318/* .IP \fBMAIL_VERBOSE\fR
319/* This is set when the -v command-line option is present.
320/* .IP \fBMAIL_CONFIG\fR
321/* The location of the configuration directory of the instance.
322/* CONFIGURATION PARAMETERS
323/* .ad
324/* .fi
325/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
326/* The default location of the Postfix main.cf and master.cf
327/* configuration files.
328/* .IP "\fBdaemon_directory (see 'postconf -d' output)\fR"
329/* The directory with Postfix support programs and daemon programs.
330/* .IP "\fBimport_environment (see 'postconf -d' output)\fR"
331/* The list of environment parameters that a Postfix process will
332/* import from a non-Postfix parent process.
333/* .IP "\fBmulti_instance_directories (empty)\fR"
334/* An optional list of non-default Postfix configuration directories;
335/* these directories belong to additional Postfix instances that share
336/* the Postfix executable files and documentation with the default
337/* Postfix instance, and that are started, stopped, etc., together
338/* with the default Postfix instance.
339/* .IP "\fBmulti_instance_group (empty)\fR"
340/* The optional instance group name of this Postfix instance.
341/* .IP "\fBmulti_instance_name (empty)\fR"
342/* The optional instance name of this Postfix instance.
343/* .IP "\fBmulti_instance_enable (no)\fR"
344/* Allow this Postfix instance to be started, stopped, etc., by a
345/* multi-instance manager.
346/* .IP "\fBpostmulti_start_commands (start)\fR"
347/* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats
348/* as "start" commands.
349/* .IP "\fBpostmulti_stop_commands (see 'postconf -d' output)\fR"
350/* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager treats
351/* as "stop" commands.
352/* .IP "\fBpostmulti_control_commands (reload flush)\fR"
353/* The \fBpostfix\fR(1) commands that the \fBpostmulti\fR(1) instance manager
354/* treats as "control" commands, that operate on running instances.
355/* .IP "\fBsyslog_facility (mail)\fR"
356/* The syslog facility of Postfix logging.
357/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
358/* The mail system name that is prepended to the process name in syslog
359/* records, so that "smtpd" becomes, for example, "postfix/smtpd".
360/* .PP
361/* Available in Postfix 3.0 and later:
362/* .IP "\fBmeta_directory (see 'postconf -d' output)\fR"
363/* The location of non-executable files that are shared among
364/* multiple Postfix instances, such as postfix-files, dynamicmaps.cf,
365/* and the multi-instance template files main.cf.proto and master.cf.proto.
366/* .IP "\fBshlib_directory (see 'postconf -d' output)\fR"
367/* The location of Postfix dynamically-linked libraries
368/* (libpostfix-*.so), and the default location of Postfix database
369/* plugins (postfix-*.so) that have a relative pathname in the
370/* dynamicmaps.cf file.
371/* FILES
372/* $meta_directory/main.cf.proto, stock configuration file
373/* $meta_directory/master.cf.proto, stock configuration file
374/* $daemon_directory/postmulti-script, life-cycle helper program
375/* SEE ALSO
376/* postfix(1), Postfix control program
377/* postfix-wrapper(5), Postfix multi-instance API
378/* README FILES
379/* .ad
380/* .fi
381/* Use "\fBpostconf readme_directory\fR" or "\fBpostconf
382/* html_directory\fR" to locate this information.
383/* .nf
384/* .na
385/* MULTI_INSTANCE_README, Postfix multi-instance management
386/* HISTORY
387/* .ad
388/* .fi
389/* The \fBpostmulti\fR(1) command was introduced with Postfix
390/* version 2.6.
391/* LICENSE
392/* .ad
393/* .fi
394/* The Secure Mailer license must be distributed with this software.
395/* AUTHOR(S)
396/* Victor Duchovni
397/* Morgan Stanley
398/*
399/* Wietse Venema
400/* IBM T.J. Watson Research
401/* P.O. Box 704
402/* Yorktown Heights, NY 10598, USA
403/*--*/
404
405/* System library. */
406
407#include <sys_defs.h>
408#include <sys/stat.h>
409#include <sys/wait.h>
410#include <vstream.h>
411#include <stdlib.h>
412#include <unistd.h>
413#include <string.h>
414#include <fcntl.h>
415#include <syslog.h>
416#include <errno.h>
417#include <ctype.h>
418#ifdef USE_PATHS_H
419#include <paths.h>
420#endif
421#include <stddef.h>
422
423/* Utility library. */
424
425#include <msg.h>
426#include <msg_vstream.h>
427#include <msg_syslog.h>
428#include <vstream.h>
429#include <vstring_vstream.h>
430#include <stringops.h>
431#include <clean_env.h>
432#include <argv.h>
433#include <safe.h>
434#include <mymalloc.h>
435#include <htable.h>
436#include <name_code.h>
437#include <ring.h>
438#include <warn_stat.h>
439
440/* Global library. */
441
442#include <mail_version.h>
443#include <mail_params.h>
444#include <mail_conf.h>
445#include <mail_parm_split.h>
446
447/* Application-specific. */
448
449 /*
450 * Configuration parameters, specific to postmulti(1).
451 */
452char *var_multi_start_cmds;
453char *var_multi_stop_cmds;
454char *var_multi_cntrl_cmds;
455
456 /*
457 * Shared directory pathnames.
458 */
459typedef struct {
460 const char *param_name;
461 char **param_value;
462} SHARED_PATH;
463
464static SHARED_PATH shared_dir_table[] = {
465 VAR_COMMAND_DIR, &var_command_dir,
466 VAR_DAEMON_DIR, &var_daemon_dir,
467 VAR_META_DIR, &var_meta_dir,
468 VAR_SHLIB_DIR, &var_shlib_dir,
469 0,
470};
471
472 /*
473 * Actions.
474 */
475#define ITER_CMD_POSTFIX (1<<0) /* postfix(1) iterator mode */
476#define ITER_CMD_LIST (1<<1) /* listing iterator mode */
477#define ITER_CMD_GENERIC (1<<2) /* generic command iterator mode */
478
479#define ITER_CMD_MASK_ALL \
480 (ITER_CMD_POSTFIX | ITER_CMD_LIST | ITER_CMD_GENERIC)
481
482#define EDIT_CMD_CREATE (1<<4) /* create new instance */
483#define EDIT_CMD_IMPORT (1<<5) /* import existing instance */
484#define EDIT_CMD_DESTROY (1<<6) /* destroy instance */
485#define EDIT_CMD_DEPORT (1<<7) /* export instance */
486#define EDIT_CMD_ENABLE (1<<8) /* enable start/stop */
487#define EDIT_CMD_DISABLE (1<<9) /* disable start/stop */
488#define EDIT_CMD_ASSIGN (1<<10) /* assign name/group */
489#define EDIT_CMD_INIT (1<<11) /* hook into main.cf */
490
491#define EDIT_CMD_MASK_ADD (EDIT_CMD_CREATE | EDIT_CMD_IMPORT)
492#define EDIT_CMD_MASK_DEL (EDIT_CMD_DESTROY | EDIT_CMD_DEPORT)
493#define EDIT_CMD_MASK_ASSIGN (EDIT_CMD_MASK_ADD | EDIT_CMD_ASSIGN)
494#define EDIT_CMD_MASK_ENB (EDIT_CMD_ENABLE | EDIT_CMD_DISABLE)
495#define EDIT_CMD_MASK_ALL \
496 (EDIT_CMD_MASK_ASSIGN | EDIT_CMD_MASK_DEL | EDIT_CMD_MASK_ENB | \
497 EDIT_CMD_INIT)
498
499 /*
500 * Edit command to number mapping, and vice versa.
501 */
502static NAME_CODE edit_command_table[] = {
503 "create", EDIT_CMD_CREATE,
504 "import", EDIT_CMD_IMPORT,
505 "destroy", EDIT_CMD_DESTROY,
506 "deport", EDIT_CMD_DEPORT,
507 "enable", EDIT_CMD_ENABLE,
508 "disable", EDIT_CMD_DISABLE,
509 "assign", EDIT_CMD_ASSIGN,
510 "init", EDIT_CMD_INIT,
511 0, -1,
512};
513
514#define EDIT_CMD_CODE(str) \
515 name_code(edit_command_table, NAME_CODE_FLAG_STRICT_CASE, (str))
516#define EDIT_CMD_STR(code) str_name_code(edit_command_table, (code))
517
518 /*
519 * Mandatory prefix for non-empty instance names.
520 */
521#ifndef NAME_PREFIX
522#define NAME_PREFIX "postfix-"
523#endif
524#define HAS_NAME_PREFIX(name) \
525 (strncmp((name), NAME_PREFIX, sizeof(NAME_PREFIX)-1) == 0)
526#define NEED_NAME_PREFIX(name) \
527 ((name) != 0 && strcmp((name), "-") != 0 && !HAS_NAME_PREFIX(name))
528#define NAME_SUFFIX(name) ((name) + sizeof(NAME_PREFIX) - 1)
529
530 /*
531 * In-core instance structure. Only private information is kept here.
532 */
533typedef struct instance {
534 RING ring; /* linkage. */
535 char *config_dir; /* private */
536 char *queue_dir; /* private */
537 char *data_dir; /* private */
538 char *name; /* null or name */
539 char *gname; /* null or group */
540 int enabled; /* start/stop enable */
541 int primary; /* special */
542} INSTANCE;
543
544 /*
545 * Managed instance list (edit mode and iterator mode).
546 */
547static RING instance_hd[1]; /* instance list head */
548
549#define RING_TO_INSTANCE(ring_ptr) RING_TO_APPL(ring_ptr, INSTANCE, ring)
550#define RING_PTR_OF(x) (&((x)->ring))
551
552#define FOREACH_INSTANCE(entry) \
553 for ((entry) = instance_hd; \
554 ((entry) = ring_succ(entry)) != instance_hd;)
555
556#define FOREACH_SECONDARY_INSTANCE(entry) \
557 for ((entry) = ring_succ(instance_hd); \
558 ((entry) = ring_succ(entry)) != instance_hd;)
559
560#define NEXT_ITERATOR_INSTANCE(flags, entry) \
561 (((flags) & ITER_FLAG_REVERSE) ? ring_pred(entry) : ring_succ(entry))
562
563#define FOREACH_ITERATOR_INSTANCE(flags, entry) \
564 for ((entry) = instance_hd; \
565 ((entry) = NEXT_ITERATOR_INSTANCE(flags, (entry))) != instance_hd;)
566
567 /*
568 * Instance selection. One can either select all instances, select by
569 * instance name, or select by instance group.
570 */
571typedef struct {
572 int type; /* see below */
573 char *name; /* undefined or name */
574} INST_SELECTION;
575
576#define INST_SEL_NONE 0 /* default: no selection */
577#define INST_SEL_ALL 1 /* select all instances */
578#define INST_SEL_NAME 2 /* select instance name */
579#define INST_SEL_GROUP 3 /* select instance group */
580
581 /*
582 * Instance name assignment. Each instance may be assigned an instance name
583 * (this must be globally unique within a multi-instance cluster) or an
584 * instance group name (this is intended to be shared). Externally, empty
585 * names may be represented as "-". Internally, we use "" only, to simplify
586 * the code.
587 */
588typedef struct {
589 char *name; /* null or assigned instance name */
590 char *gname; /* null or assigned group name */
591} NAME_ASSIGNMENT;
592
593 /*
594 * Iterator controls for non-edit commands. One can reverse the iteration
595 * order, or give special treatment to disabled instances.
596 */
597#define ITER_FLAG_DEFAULT 0 /* default setting */
598#define ITER_FLAG_REVERSE (1<<0) /* reverse iteration order */
599#define ITER_FLAG_CHECK_DISABLED (1<<1) /* check disabled instances */
600#define ITER_FLAG_SKIP_DISABLED (1<<2) /* skip disabled instances */
601
602 /*
603 * Environment export controls for edit commands. postmulti(1) exports only
604 * things that need to be updated.
605 */
606#define EXP_FLAG_MULTI_DIRS (1<<0) /* export multi_instance_directories */
607#define EXP_FLAG_MULTI_NAME (1<<1) /* export multi_instance_name */
608#define EXP_FLAG_MULTI_GROUP (1<<2) /* export multi_instance_group */
609
610 /*
611 * To detect conflicts, each instance name and each shared or private
612 * pathname is registered in one place, with its owner. Everyone must
613 * register their claims when they join, and will be rejected in case of
614 * conlict.
615 *
616 * Each claim value involves a parameter value (either a directory name or an
617 * instance name). Each claim owner is the config_directory pathname plus
618 * the parameter name.
619 *
620 * XXX: No multi.cf lock file, so this is not race-free.
621 */
622static HTABLE *claim_table;
623
624#define IS_CLAIMED_BY(name) \
625 (claim_table ? htable_find(claim_table, (name)) : 0)
626
627 /*
628 * Forward references.
629 */
630static int iterate_command(int, int, char **, INST_SELECTION *);
631static int match_instance_selection(INSTANCE *, INST_SELECTION *);
632
633 /*
634 * Convenience.
635 */
636#define INSTANCE_NAME(i) ((i)->name ? (i)->name : (i)->config_dir)
637#define STR(buf) vstring_str(buf)
638
639/* register_claim - register claim or bust */
640
641static void register_claim(const char *instance_path, const char *param_name,
642 const char *param_value)
643{
644 const char *myname = "register_claim";
645 char *requestor;
646 const char *owner;
647
648 /*
649 * Sanity checks.
650 */
651 if (instance_path == 0 || *instance_path == 0)
652 msg_panic("%s: no or empty instance pathname", myname);
653 if (param_name == 0 || *param_name == 0)
654 msg_panic("%s: no or empty parameter name", myname);
655 if (param_value == 0)
656 msg_panic("%s: no parameter value", myname);
657
658 /*
659 * Make a claim or report a conflict.
660 */
661 if (claim_table == 0)
662 claim_table = htable_create(100);
663 requestor = concatenate(instance_path, ", ", param_name, (char *) 0);
664 if ((owner = htable_find(claim_table, param_value)) == 0) {
665 (void) htable_enter(claim_table, param_value, requestor);
666 } else if (strcmp(owner, requestor) == 0) {
667 myfree(requestor);
668 } else {
669 msg_fatal("instance %s, %s=%s conflicts with instance %s=%s",
670 instance_path, param_name, param_value, owner, param_value);
671 }
672}
673
674/* claim_instance_attributes - claim multiple private instance attributes */
675
676static void claim_instance_attributes(INSTANCE *ip)
677{
678
679 /*
680 * Detect instance name or pathname conflicts between this instance and
681 * other instances. XXX: No multi.cf lock file, so this is not race-free.
682 */
683 if (ip->name)
684 register_claim(ip->config_dir, VAR_MULTI_NAME, ip->name);
685 register_claim(ip->config_dir, VAR_CONFIG_DIR, ip->config_dir);
686 register_claim(ip->config_dir, VAR_QUEUE_DIR, ip->queue_dir);
687 register_claim(ip->config_dir, VAR_DATA_DIR, ip->data_dir);
688}
689
690/* alloc_instance - allocate a single instance object */
691
692static INSTANCE *alloc_instance(const char *config_dir)
693{
694 INSTANCE *ip = (INSTANCE *) mymalloc(sizeof(INSTANCE));
695
696 ring_init(RING_PTR_OF(ip));
697 ip->config_dir = config_dir ? mystrdup(config_dir) : 0;
698 ip->queue_dir = 0;
699 ip->data_dir = 0;
700 ip->name = 0;
701 ip->gname = 0;
702 ip->enabled = 0;
703 ip->primary = 0;
704
705 return (ip);
706}
707
708#if 0
709
710/* free_instance - free a single instance object */
711
712static void free_instance(INSTANCE *ip)
713{
714
715 /*
716 * If we continue after secondary main.cf file read error, we must be
717 * prepared for the case that some parameters may be missing.
718 */
719 if (ip->name)
720 myfree(ip->name);
721 if (ip->gname)
722 myfree(ip->gname);
723 if (ip->config_dir)
724 myfree(ip->config_dir);
725 if (ip->queue_dir)
726 myfree(ip->queue_dir);
727 if (ip->data_dir)
728 myfree(ip->data_dir);
729 myfree((void *) ip);
730}
731
732#endif
733
734/* insert_instance - insert instance before selected location, claim names */
735
736static void insert_instance(INSTANCE *ip, INST_SELECTION *selection)
737{
738 RING *old;
739
740#define append_instance(ip) insert_instance((ip), (INST_SELECTION *) 0)
741
742 /*
743 * Insert instance before the selected site.
744 */
745 claim_instance_attributes(ip);
746 if (ring_succ(instance_hd) == 0)
747 ring_init(instance_hd);
748 if (selection && selection->type != INST_SEL_NONE) {
749 FOREACH_SECONDARY_INSTANCE(old) {
750 if (match_instance_selection(RING_TO_INSTANCE(old), selection)) {
751 ring_prepend(old, RING_PTR_OF(ip));
752 return;
753 }
754 }
755 if (selection->type != INST_SEL_ALL)
756 msg_fatal("No matching secondary instances");
757 }
758 ring_prepend(instance_hd, RING_PTR_OF(ip));
759}
760
761/* create_primary_instance - synthetic entry for primary instance */
762
763static INSTANCE *create_primary_instance(void)
764{
765 INSTANCE *ip = alloc_instance(var_config_dir);
766
767 /*
768 * There is no need to load primary instance paramater settings from
769 * file. We already have the main.cf parameters of interest in memory.
770 */
771#define SAVE_INSTANCE_NAME(val) (*(val) ? mystrdup(val) : 0)
772
773 ip->name = SAVE_INSTANCE_NAME(var_multi_name);
774 ip->gname = SAVE_INSTANCE_NAME(var_multi_group);
775 ip->enabled = var_multi_enable;
776 ip->queue_dir = mystrdup(var_queue_dir);
777 ip->data_dir = mystrdup(var_data_dir);
778 ip->primary = 1;
779 return (ip);
780}
781
782/* load_instance - read instance parameters from config_dir/main.cf */
783
784static INSTANCE *load_instance(INSTANCE *ip)
785{
786 VSTREAM *pipe;
787 VSTRING *buf;
788 char *name;
789 char *value;
790 ARGV *cmd;
791 int count = 0;
792 static NAME_CODE bool_code[] = {
793 CONFIG_BOOL_YES, 1,
794 CONFIG_BOOL_NO, 0,
795 0, -1,
796 };
797
798 /*
799 * Expand parameter values in the context of the target main.cf file.
800 */
801#define REQUEST_PARAM_COUNT 5 /* # of requested parameters */
802
803 cmd = argv_alloc(REQUEST_PARAM_COUNT + 3);
804 name = concatenate(var_command_dir, "/", "postconf", (char *) 0);
805 argv_add(cmd, name, "-xc", ip->config_dir,
806 VAR_QUEUE_DIR, VAR_DATA_DIR,
807 VAR_MULTI_NAME, VAR_MULTI_GROUP, VAR_MULTI_ENABLE,
808 (char *) 0);
809 myfree(name);
810 pipe = vstream_popen(O_RDONLY, CA_VSTREAM_POPEN_ARGV(cmd->argv),
811 CA_VSTREAM_POPEN_END);
812 argv_free(cmd);
813 if (pipe == 0)
814 msg_fatal("Cannot parse %s/main.cf file: %m", ip->config_dir);
815
816 /*
817 * Read parameter settings from postconf. See also comments below on
818 * whether we should continue or skip groups after error instead of
819 * bailing out immediately.
820 */
821 buf = vstring_alloc(100);
822 while (vstring_get_nonl(buf, pipe) != VSTREAM_EOF) {
823 if (split_nameval(STR(buf), &name, &value))
824 msg_fatal("Invalid %s/main.cf parameter: %s",
825 ip->config_dir, STR(buf));
826 if (strcmp(name, VAR_QUEUE_DIR) == 0 && ++count)
827 ip->queue_dir = mystrdup(value);
828 else if (strcmp(name, VAR_DATA_DIR) == 0 && ++count)
829 ip->data_dir = mystrdup(value);
830 else if (strcmp(name, VAR_MULTI_NAME) == 0 && ++count)
831 ip->name = SAVE_INSTANCE_NAME(value);
832 else if (strcmp(name, VAR_MULTI_GROUP) == 0 && ++count)
833 ip->gname = SAVE_INSTANCE_NAME(value);
834 else if (strcmp(name, VAR_MULTI_ENABLE) == 0 && ++count) {
835 /* mail_conf_bool(3) is case insensitive! */
836 ip->enabled = name_code(bool_code, NAME_CODE_FLAG_NONE, value);
837 if (ip->enabled < 0)
838 msg_fatal("Unexpected %s/main.cf entry: %s = %s",
839 ip->config_dir, VAR_MULTI_ENABLE, value);
840 }
841 }
842 vstring_free(buf);
843
844 /*
845 * XXX We should not bail out while reading a bad secondary main.cf file.
846 * When we manage dozens or more instances, the likelihood increases that
847 * some file will be damaged or missing after a system crash. That is not
848 * a good reason to prevent undamaged Postfix instances from starting.
849 */
850 if (count != REQUEST_PARAM_COUNT)
851 msg_fatal("Failed to obtain all required %s/main.cf parameters",
852 ip->config_dir);
853
854 if (vstream_pclose(pipe))
855 msg_fatal("Cannot parse %s/main.cf file", ip->config_dir);
856 return (ip);
857}
858
859/* load_all_instances - compute list of Postfix instances */
860
861static void load_all_instances(void)
862{
863 INSTANCE *primary_instance;
864 char **cpp;
865 ARGV *secondary_names;
866
867 /*
868 * Avoid unexpected behavior when $multi_instance_directories contains
869 * only comma characters. Count the actual number of elements, before we
870 * decide that the list is empty.
871 */
872 secondary_names = argv_split(var_multi_conf_dirs, CHARS_COMMA_SP);
873
874 /*
875 * First, the primary instance. This is synthesized out of thin air.
876 */
877 primary_instance = create_primary_instance();
878 if (secondary_names->argc == 0)
879 primary_instance->enabled = 1; /* Single-instance mode */
880 append_instance(primary_instance);
881
882 /*
883 * Next, instances defined in $multi_instance_directories. Note:
884 * load_instance() has side effects on the global config dictionary, but
885 * this does not affect the values that have already been extracted into
886 * C variables.
887 */
888 for (cpp = secondary_names->argv; *cpp != 0; cpp++)
889 append_instance(load_instance(alloc_instance(*cpp)));
890
891 argv_free(secondary_names);
892}
893
894/* match_instance_selection - match all/name/group constraints */
895
896static int match_instance_selection(INSTANCE *ip, INST_SELECTION *selection)
897{
898 char *iname;
899 char *name;
900
901 /*
902 * When selecting (rather than assigning names) an instance, we match by
903 * the instance name, config_directory path, or the instance name suffix
904 * (name without mandatory prefix). Selecting "-" selects the primary
905 * instance.
906 */
907 switch (selection->type) {
908 case INST_SEL_NONE:
909 return (0);
910 case INST_SEL_ALL:
911 return (1);
912 case INST_SEL_GROUP:
913 return (ip->gname != 0 && strcmp(selection->name, ip->gname) == 0);
914 case INST_SEL_NAME:
915 name = selection->name;
916 if (*name == '/' || ip->name == 0)
917 iname = ip->config_dir;
918 else if (!HAS_NAME_PREFIX(name) && HAS_NAME_PREFIX(ip->name))
919 iname = NAME_SUFFIX(ip->name);
920 else
921 iname = ip->name;
922 return (strcmp(name, iname) == 0
923 || (ip->primary && strcmp(name, "-") == 0));
924 default:
925 msg_panic("match_instance_selection: unknown selection type: %d",
926 selection->type);
927 }
928}
929
930/* check_setenv - setenv() with extreme prejudice */
931
932static void check_setenv(const char *name, const char *value)
933{
934#define CLOBBER 1
935 if (setenv(name, value, CLOBBER) < 0)
936 msg_fatal("setenv: %m");
937}
938
939/* prepend_command_path - prepend command_directory to PATH */
940
941static void prepend_command_path(void)
942{
943 char *cmd_path;
944
945 /*
946 * Carefully prepend "$command_directory:" to PATH. We can free the
947 * buffer after check_setenv(), since the value is copied there.
948 */
949 cmd_path = safe_getenv("PATH");
950 cmd_path = concatenate(var_command_dir, ":", (cmd_path && *cmd_path) ?
951 cmd_path : ROOT_PATH, (char *) 0);
952 check_setenv("PATH", cmd_path);
953 myfree(cmd_path);
954}
955
956/* check_shared_dir_status - check and claim shared directories */
957
958static void check_shared_dir_status(void)
959{
960 struct stat st;
961 const SHARED_PATH *sp;
962
963 /*
964 * XXX Avoid false conflicts with meta_directory. This usually overlaps
965 * with other directories, typcally config_directory, shlib_directory or
966 * daemon_directory.
967 */
968 for (sp = shared_dir_table; sp->param_name; ++sp) {
969 if (sp->param_value[0][0] != '/') /* "no" or other special */
970 continue;
971 if (stat(sp->param_value[0], &st) < 0)
972 msg_fatal("%s = '%s': directory not found: %m",
973 sp->param_name, sp->param_value[0]);
974 if (!S_ISDIR(st.st_mode))
975 msg_fatal("%s = '%s' is not a directory",
976 sp->param_name, sp->param_value[0]);
977 if (strcmp(sp->param_name, VAR_META_DIR) == 0)
978 continue;
979 register_claim(var_config_dir, sp->param_name, sp->param_value[0]);
980 }
981}
982
983/* check_safe_name - allow instance or group name with only "safe" characters */
984
985static int check_safe_name(const char *s)
986{
987#define SAFE_PUNCT "!@%-_=+:./"
988 if (*s == 0)
989 return (0);
990 for (; *s; ++s) {
991 if (!ISALNUM(*s) && !strchr(SAFE_PUNCT, *s))
992 return (0);
993 }
994 return (1);
995}
996
997/* check_name_assignments - Check validity of assigned instance or group name */
998
999static void check_name_assignments(NAME_ASSIGNMENT *assignment)
1000{
1001
1002 /*
1003 * Syntax check the assigned instance name. This name is also used to
1004 * generate directory pathnames, so we must not allow "/" characters.
1005 *
1006 * The value "" will clear the name and is always valid. The command-line
1007 * parser has already converted "-" into "", to simplify implementation.
1008 */
1009 if (assignment->name && *assignment->name) {
1010 if (!check_safe_name(assignment->name))
1011 msg_fatal("Unsafe characters in new instance name: '%s'",
1012 assignment->name);
1013 if (strchr(assignment->name, '/'))
1014 msg_fatal("Illegal '/' character in new instance name: '%s'",
1015 assignment->name);
1016 if (NEED_NAME_PREFIX(assignment->name))
1017 msg_fatal("New instance name must start with '%s'",
1018 NAME_PREFIX);
1019 }
1020
1021 /*
1022 * Syntax check the assigned group name.
1023 */
1024 if (assignment->gname && *assignment->gname) {
1025 if (!check_safe_name(assignment->gname))
1026 msg_fatal("Unsafe characters in '-G %s'", assignment->gname);
1027 }
1028}
1029
1030/* do_name_assignments - assign instance/group names */
1031
1032static int do_name_assignments(INSTANCE *target, NAME_ASSIGNMENT *assignment)
1033{
1034 int export_flags = 0;
1035
1036 /*
1037 * The command-line parser has already converted "-" into "", to simplify
1038 * implementation.
1039 */
1040 if (assignment->name
1041 && strcmp(assignment->name, target->name ? target->name : "")) {
1042 register_claim(target->config_dir, VAR_MULTI_NAME, assignment->name);
1043 if (target->name)
1044 myfree(target->name);
1045 target->name = SAVE_INSTANCE_NAME(assignment->name);
1046 export_flags |= EXP_FLAG_MULTI_NAME;
1047 }
1048 if (assignment->gname
1049 && strcmp(assignment->gname, target->gname ? target->gname : "")) {
1050 if (target->gname)
1051 myfree(target->gname);
1052 target->gname = SAVE_INSTANCE_NAME(assignment->gname);
1053 export_flags |= EXP_FLAG_MULTI_GROUP;
1054 }
1055 return (export_flags);
1056}
1057
1058/* make_private_path - generate secondary pathname using primary as template */
1059
1060static char *make_private_path(const char *param_name,
1061 const char *primary_value,
1062 NAME_ASSIGNMENT *assignment)
1063{
1064 char *path;
1065 char *base;
1066 char *end;
1067
1068 /*
1069 * The command-line parser has already converted "-" into "", to simplify
1070 * implementation.
1071 */
1072 if (assignment->name == 0 || *assignment->name == 0)
1073 msg_fatal("Missing %s parameter value", param_name);
1074
1075 if (*primary_value != '/')
1076 msg_fatal("Invalid default %s parameter value: '%s': "
1077 "specify an absolute pathname",
1078 param_name, primary_value);
1079
1080 base = mystrdup(primary_value);
1081 if ((end = strrchr(base, '/')) != 0) {
1082 /* Drop trailing slashes */
1083 if (end[1] == '\0') {
1084 while (--end > base && *end == '/')
1085 *end = '\0';
1086 end = strrchr(base, '/');
1087 }
1088 /* Drop last path component */
1089 while (end > base && *end == '/')
1090 *end-- = '\0';
1091 }
1092 path = concatenate(base[1] ? base : "", "/",
1093 assignment->name, (char *) 0);
1094 myfree(base);
1095 return (path);
1096}
1097
1098/* assign_new_parameter - assign new instance private name=value */
1099
1100static void assign_new_parameter(INSTANCE *new, int edit_cmd,
1101 const char *arg)
1102{
1103 char *saved_arg;
1104 char *name;
1105 char *value;
1106 char *end;
1107 char **target = 0;
1108
1109 /*
1110 * With "import", only config_directory is specified on the command line
1111 * (either explicitly as config_directory=/path/name, or implicitly as
1112 * instance name). The other private directory pathnames are taken from
1113 * the existing instance's main.cf file.
1114 *
1115 * With "create", all private pathname parameters are specified on the
1116 * command line, or generated from an instance name.
1117 */
1118 saved_arg = mystrdup(arg);
1119 if (split_nameval(saved_arg, &name, &value))
1120 msg_fatal("Malformed parameter setting '%s'", arg);
1121
1122 if (strcmp(VAR_CONFIG_DIR, name) == 0) {
1123 target = &new->config_dir;
1124 } else if (edit_cmd != EDIT_CMD_IMPORT) {
1125 if (strcmp(VAR_QUEUE_DIR, name) == 0) {
1126 target = &new->queue_dir;
1127 } else if (strcmp(VAR_DATA_DIR, name) == 0) {
1128 target = &new->data_dir;
1129 }
1130 }
1131 if (target == 0)
1132 msg_fatal("Parameter '%s' not valid with action %s",
1133 name, EDIT_CMD_STR(edit_cmd));
1134
1135 /*
1136 * Extract and assign the parameter value. We do a limited number of
1137 * checks here. Conflicts between instances are checked by the caller.
1138 * More checks may be implemented in the helper script if inspired.
1139 */
1140 if (*value != '/')
1141 msg_fatal("Parameter setting '%s' is not an absolute path", name);
1142
1143 /* Tolerate+trim trailing "/" from readline completion */
1144 for (end = value + strlen(value) - 1; end > value && *end == '/'; --end)
1145 *end = 0;
1146
1147 /* No checks here for "/." or other shoot-foot silliness. */
1148 if (end == value)
1149 msg_fatal("Parameter setting '%s' is the root directory", name);
1150
1151 if (*target)
1152 myfree(*target);
1153 *target = mystrdup(value);
1154
1155 /*
1156 * Cleanup.
1157 */
1158 myfree(saved_arg);
1159}
1160
1161/* assign_new_parameters - initialize new instance private parameters */
1162
1163static void assign_new_parameters(INSTANCE *new, int edit_cmd,
1164 char **argv, NAME_ASSIGNMENT *assignment)
1165{
1166 const char *owner;
1167
1168 /*
1169 * Sanity check the explicit parameter settings. More stringent checks
1170 * may take place in the helper script.
1171 */
1172 while (*argv)
1173 assign_new_parameter(new, edit_cmd, *argv++);
1174
1175 /*
1176 * Initialize any missing private directory pathnames, using the primary
1177 * configuration directory parameter values as a template, and using the
1178 * assigned instance name to fill in the blanks.
1179 *
1180 * When importing an existing instance, load private directory pathnames
1181 * from its main.cf file.
1182 */
1183 if (new->config_dir == 0)
1184 new->config_dir =
1185 make_private_path(VAR_CONFIG_DIR, var_config_dir, assignment);
1186 /* Needed for better-quality error message. */
1187 if ((owner = IS_CLAIMED_BY(new->config_dir)) != 0)
1188 msg_fatal("new %s=%s is already in use by instance %s=%s",
1189 VAR_CONFIG_DIR, new->config_dir, owner, new->config_dir);
1190 if (edit_cmd != EDIT_CMD_IMPORT) {
1191 if (new->queue_dir == 0)
1192 new->queue_dir =
1193 make_private_path(VAR_QUEUE_DIR, var_queue_dir, assignment);
1194 if (new->data_dir == 0)
1195 new->data_dir =
1196 make_private_path(VAR_DATA_DIR, var_data_dir, assignment);
1197 } else {
1198 load_instance(new);
1199 }
1200}
1201
1202/* export_helper_environment - update environment settings for helper command */
1203
1204static void export_helper_environment(INSTANCE *target, int export_flags)
1205{
1206 ARGV *import_env;
1207 VSTRING *multi_dirs;
1208 const SHARED_PATH *sp;
1209 RING *entry;
1210
1211 /*
1212 * Environment import filter, to enforce consistent behavior whether this
1213 * command is started by hand, or at system boot time. This is necessary
1214 * because some shell scripts use environment settings to override
1215 * main.cf settings.
1216 */
1217 import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ);
1218 clean_env(import_env->argv);
1219 argv_free(import_env);
1220
1221 /*
1222 * Prepend $command_directory: to PATH. This supposedly ensures that
1223 * naive programs will execute commands from the right Postfix version.
1224 */
1225 prepend_command_path();
1226
1227 /*
1228 * The following ensures that Postfix's own programs will target the
1229 * primary instance.
1230 */
1231 check_setenv(CONF_ENV_PATH, var_config_dir);
1232
1233 /*
1234 * Export the parameter settings that are shared between instances.
1235 */
1236 for (sp = shared_dir_table; sp->param_name; ++sp)
1237 check_setenv(sp->param_name, sp->param_value[0]);
1238
1239 /*
1240 * Export the target instance's private directory locations.
1241 */
1242 check_setenv(VAR_CONFIG_DIR, target->config_dir);
1243 check_setenv(VAR_QUEUE_DIR, target->queue_dir);
1244 check_setenv(VAR_DATA_DIR, target->data_dir);
1245
1246 /*
1247 * With operations that add or delete a secondary instance, we export the
1248 * modified multi_instance_directories parameter value for the primary
1249 * Postfix instance.
1250 */
1251 if (export_flags & EXP_FLAG_MULTI_DIRS) {
1252 multi_dirs = vstring_alloc(100);
1253 FOREACH_SECONDARY_INSTANCE(entry) {
1254 if (VSTRING_LEN(multi_dirs) > 0)
1255 VSTRING_ADDCH(multi_dirs, ' ');
1256 vstring_strcat(multi_dirs, RING_TO_INSTANCE(entry)->config_dir);
1257 }
1258 check_setenv(VAR_MULTI_CONF_DIRS, STR(multi_dirs));
1259 vstring_free(multi_dirs);
1260 }
1261
1262 /*
1263 * Export updates for the instance name and group. Empty value (or no
1264 * export) means don't update, "-" means clear.
1265 */
1266 if (export_flags & EXP_FLAG_MULTI_NAME)
1267 check_setenv(VAR_MULTI_NAME, target->name && *target->name ?
1268 target->name : "-");
1269
1270 if (export_flags & EXP_FLAG_MULTI_GROUP)
1271 check_setenv(VAR_MULTI_GROUP, target->gname && *target->gname ?
1272 target->gname : "-");
1273
1274 /*
1275 * If we would implement enable/disable commands by exporting the updated
1276 * parameter value, then we could skip commands that have no effect, just
1277 * like we can skip "assign" commands that make no change.
1278 */
1279}
1280
1281/* install_new_instance - install and return newly created instance */
1282
1283static INSTANCE *install_new_instance(int edit_cmd, char **argv,
1284 INST_SELECTION *selection,
1285 NAME_ASSIGNMENT *assignment,
1286 int *export_flags)
1287{
1288 INSTANCE *new;
1289
1290 new = alloc_instance((char *) 0);
1291 check_name_assignments(assignment);
1292 assign_new_parameters(new, edit_cmd, argv, assignment);
1293 *export_flags |=
1294 (do_name_assignments(new, assignment) | EXP_FLAG_MULTI_DIRS);
1295 insert_instance(new, selection);
1296 return (new);
1297}
1298
1299/* update_instance - update existing instance, return export flags */
1300
1301static int update_instance(INSTANCE *target, NAME_ASSIGNMENT *assignment)
1302{
1303 int export_flags;
1304
1305 check_name_assignments(assignment);
1306 export_flags = do_name_assignments(target, assignment);
1307 return (export_flags);
1308}
1309
1310/* select_existing_instance - return instance selected for management */
1311
1312static INSTANCE *select_existing_instance(INST_SELECTION *selection,
1313 int unlink_flag,
1314 int *export_flags)
1315{
1316 INSTANCE *selected = 0;
1317 RING *entry;
1318 INSTANCE *ip;
1319
1320#define DONT_UNLINK 0
1321#define DO_UNLINK 1
1322
1323 if (selection->type != INST_SEL_NAME)
1324 msg_fatal("Select an instance via '-i name'");
1325
1326 /* Find the selected instance and its predecessor */
1327 FOREACH_INSTANCE(entry) {
1328 if (match_instance_selection(ip = RING_TO_INSTANCE(entry), selection)) {
1329 selected = ip;
1330 break;
1331 }
1332 }
1333
1334 if (selected == 0)
1335 msg_fatal("No instance named %s", selection->name);
1336
1337 if (unlink_flag) {
1338 /* Splice the target instance out of the list */
1339 if (ring_pred(entry) == instance_hd)
1340 msg_fatal("Cannot remove the primary instance");
1341 if (selected->enabled)
1342 msg_fatal("Cannot remove enabled instances");
1343 ring_detach(entry);
1344 if (export_flags == 0)
1345 msg_panic("select_existing_instance: no export flags");
1346 *export_flags |= EXP_FLAG_MULTI_DIRS;
1347 }
1348 return (selected);
1349}
1350
1351/* manage - create/destroy/... manage instances */
1352
1353static NORETURN manage(int edit_cmd, int argc, char **argv,
1354 INST_SELECTION *selection,
1355 NAME_ASSIGNMENT *assignment)
1356{
1357 char *cmd;
1358 INSTANCE *target;
1359 int export_flags;
1360
1361 /*
1362 * Edit mode is not subject to iterator controls.
1363 */
1364#define NO_EXPORT_FLAGS ((int *) 0)
1365 export_flags = 0;
1366
1367 switch (edit_cmd) {
1368 case EDIT_CMD_INIT:
1369 target = create_primary_instance();
1370 break;
1371
1372 case EDIT_CMD_CREATE:
1373 case EDIT_CMD_IMPORT:
1374 load_all_instances();
1375 target = install_new_instance(edit_cmd, argv, selection,
1376 assignment, &export_flags);
1377 break;
1378
1379 case EDIT_CMD_ASSIGN:
1380 load_all_instances();
1381 target =
1382 select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS);
1383 export_flags |= update_instance(target, assignment);
1384 if (export_flags == 0)
1385 exit(0);
1386 break;
1387
1388 case EDIT_CMD_DESTROY:
1389 case EDIT_CMD_DEPORT:
1390 load_all_instances();
1391 target = select_existing_instance(selection, DO_UNLINK, &export_flags);
1392 break;
1393
1394 default:
1395 load_all_instances();
1396 target =
1397 select_existing_instance(selection, DONT_UNLINK, NO_EXPORT_FLAGS);
1398 break;
1399 }
1400
1401 /*
1402 * Set up the helper script's process environment, and execute the helper
1403 * script.
1404 */
1405#define HELPER "postmulti-script"
1406
1407 export_helper_environment(target, export_flags);
1408 cmd = concatenate(var_daemon_dir, "/" HELPER, (char *) 0);
1409 execl(cmd, cmd, "-e", EDIT_CMD_STR(edit_cmd), (char *) 0);
1410 msg_fatal("%s: %m", cmd);
1411}
1412
1413/* run_user_command - execute external command with requested MAIL_CONFIG env */
1414
1415static int run_user_command(INSTANCE *ip, int iter_cmd, int iter_flags,
1416 char **argv)
1417{
1418 WAIT_STATUS_T status;
1419 int pid;
1420 int wpid;
1421
1422 /*
1423 * Set up a process environment. The postfix(1) command needs MAIL_CONFIG
1424 * (or the equivalent command-line option); it overrides everything else.
1425 *
1426 * postmulti(1) typically runs various Postfix utilities (postsuper, ...) in
1427 * the context of one or more instances. It can also run various scripts
1428 * on the users PATH. So we can't clobber the user's PATH, but do want to
1429 * make sure that the utilities in $command_directory are always found in
1430 * the right place (or at all).
1431 */
1432 switch (pid = fork()) {
1433 case -1:
1434 msg_warn("fork %s: %m", argv[0]);
1435 return -1;
1436 case 0:
1437 check_setenv(CONF_ENV_PATH, ip->config_dir);
1438 if (iter_cmd != ITER_CMD_POSTFIX) {
1439 check_setenv(VAR_DAEMON_DIR, var_daemon_dir);
1440 check_setenv(VAR_COMMAND_DIR, var_command_dir);
1441 check_setenv(VAR_CONFIG_DIR, ip->config_dir);
1442 check_setenv(VAR_QUEUE_DIR, ip->queue_dir);
1443 check_setenv(VAR_DATA_DIR, ip->data_dir);
1444 check_setenv(VAR_MULTI_NAME, ip->name ? ip->name : "");
1445 check_setenv(VAR_MULTI_GROUP, ip->gname ? ip->gname : "");
1446 check_setenv(VAR_MULTI_ENABLE, ip->enabled ?
1447 CONFIG_BOOL_YES : CONFIG_BOOL_NO);
1448 prepend_command_path();
1449 }
1450
1451 /*
1452 * Replace: postfix -- start ... With: postfix -- check ...
1453 */
1454 if (iter_cmd == ITER_CMD_POSTFIX
1455 && (iter_flags & ITER_FLAG_CHECK_DISABLED) && !ip->enabled)
1456 argv[2] = "check";
1457
1458 execvp(argv[0], argv);
1459 msg_fatal("execvp %s: %m", argv[0]);
1460 default:
1461 do {
1462 wpid = waitpid(pid, &status, 0);
1463 } while (wpid == -1 && errno == EINTR);
1464 return (wpid == -1 ? -1 :
1465 WIFEXITED(status) ? WEXITSTATUS(status) : 1);
1466 }
1467}
1468
1469/* word_in_list - look up command in start, stop, or control list */
1470
1471static int word_in_list(char *cmdlist, const char *cmd)
1472{
1473 char *saved;
1474 char *cp;
1475 char *elem;
1476
1477 cp = saved = mystrdup(cmdlist);
1478 while ((elem = mystrtok(&cp, CHARS_COMMA_SP)) != 0 && strcmp(elem, cmd) != 0)
1479 /* void */ ;
1480 myfree(saved);
1481 return (elem != 0);
1482}
1483
1484/* iterate_postfix_command - execute postfix(1) command */
1485
1486static int iterate_postfix_command(int iter_cmd, int argc, char **argv,
1487 INST_SELECTION *selection)
1488{
1489 int exit_status;
1490 char *cmd;
1491 ARGV *my_argv;
1492 int iter_flags;
1493
1494 /*
1495 * Override the iterator controls.
1496 */
1497 if (word_in_list(var_multi_start_cmds, argv[0])) {
1498 iter_flags = ITER_FLAG_CHECK_DISABLED;
1499 } else if (word_in_list(var_multi_stop_cmds, argv[0])) {
1500 iter_flags = ITER_FLAG_SKIP_DISABLED | ITER_FLAG_REVERSE;
1501 } else if (word_in_list(var_multi_cntrl_cmds, argv[0])) {
1502 iter_flags = ITER_FLAG_SKIP_DISABLED;
1503 } else {
1504 iter_flags = 0;
1505 }
1506
1507 /*
1508 * Override the command line in a straightforward manner: prepend
1509 * "postfix --" to the command arguments. Other overrides (environment,
1510 * start -> check) are implemented below the iterator.
1511 */
1512#define POSTFIX_CMD "postfix"
1513
1514 my_argv = argv_alloc(argc + 2);
1515 cmd = concatenate(var_command_dir, "/" POSTFIX_CMD, (char *) 0);
1516 argv_add(my_argv, cmd, "--", (char *) 0);
1517 myfree(cmd);
1518 while (*argv)
1519 argv_add(my_argv, *argv++, (char *) 0);
1520
1521 /*
1522 * Execute the command for all applicable Postfix instances.
1523 */
1524 exit_status =
1525 iterate_command(iter_cmd, iter_flags, my_argv->argv, selection);
1526
1527 argv_free(my_argv);
1528 return (exit_status);
1529}
1530
1531/* list_instances - list all selected instances */
1532
1533static void list_instances(int iter_flags, INST_SELECTION *selection)
1534{
1535 RING *entry;
1536 INSTANCE *ip;
1537
1538 /*
1539 * Iterate over the selected instances.
1540 */
1541 FOREACH_ITERATOR_INSTANCE(iter_flags, entry) {
1542 ip = RING_TO_INSTANCE(entry);
1543 if (match_instance_selection(ip, selection))
1544 vstream_printf("%-15s %-15s %-9s %s\n",
1545 ip->name ? ip->name : "-",
1546 ip->gname ? ip->gname : "-",
1547 ip->enabled ? "y" : "n",
1548 ip->config_dir);
1549 }
1550 if (vstream_fflush(VSTREAM_OUT))
1551 msg_fatal("error writing output: %m");
1552}
1553
1554/* iterate_command - execute command for selected instances */
1555
1556static int iterate_command(int iter_cmd, int iter_flags, char **argv,
1557 INST_SELECTION *selection)
1558{
1559 int exit_status = 0;
1560 int matched = 0;
1561 RING *entry;
1562 INSTANCE *ip;
1563
1564 /*
1565 * Iterate over the selected instances.
1566 */
1567 FOREACH_ITERATOR_INSTANCE(iter_flags, entry) {
1568 ip = RING_TO_INSTANCE(entry);
1569 if ((iter_flags & ITER_FLAG_SKIP_DISABLED) && !ip->enabled)
1570 continue;
1571 if (!match_instance_selection(ip, selection))
1572 continue;
1573 matched = 1;
1574
1575 /* Run the requested command */
1576 if (run_user_command(ip, iter_cmd, iter_flags, argv) != 0)
1577 exit_status = 1;
1578 }
1579 if (matched == 0)
1580 msg_fatal("No matching instances");
1581
1582 return (exit_status);
1583}
1584
1585/* iterate - Iterate over all or selected instances */
1586
1587static NORETURN iterate(int iter_cmd, int iter_flags, int argc, char **argv,
1588 INST_SELECTION *selection)
1589{
1590 int exit_status;
1591
1592 /*
1593 * In iterator mode, no selection means wild-card selection.
1594 */
1595 if (selection->type == INST_SEL_NONE)
1596 selection->type = INST_SEL_ALL;
1597
1598 /*
1599 * Load the in-memory instance table from main.cf files.
1600 */
1601 load_all_instances();
1602
1603 /*
1604 * Iterate over the selected instances.
1605 */
1606 switch (iter_cmd) {
1607 case ITER_CMD_POSTFIX:
1608 exit_status = iterate_postfix_command(iter_cmd, argc, argv, selection);
1609 break;
1610 case ITER_CMD_LIST:
1611 list_instances(iter_flags, selection);
1612 exit_status = 0;
1613 break;
1614 case ITER_CMD_GENERIC:
1615 exit_status = iterate_command(iter_cmd, iter_flags, argv, selection);
1616 break;
1617 default:
1618 msg_panic("iterate: unknown mode: %d", iter_cmd);
1619 }
1620 exit(exit_status);
1621}
1622
1623static NORETURN usage(const char *progname)
1624{
1625 msg_fatal("Usage:"
1626 "%s -l [-v] [-a] [-g group] [-i instance] | "
1627 "%s -p [-v] [-a] [-g group] [-i instance] command... | "
1628 "%s -x [-v] [-a] [-i name] [-g group] command... | "
1629 "%s -e action [-v] [-a] [-i name] [-g group] [-I name] "
1630 "[-G group] [param=value ...]",
1631 progname, progname, progname, progname);
1632}
1633
1634MAIL_VERSION_STAMP_DECLARE;
1635
1636/* main - iterate commands over multiple instance or manage instances */
1637
1638int main(int argc, char **argv)
1639{
1640 int fd;
1641 struct stat st;
1642 char *slash;
1643 char *config_dir;
1644 int ch;
1645 static const CONFIG_STR_TABLE str_table[] = {
1646 VAR_MULTI_START_CMDS, DEF_MULTI_START_CMDS, &var_multi_start_cmds, 0, 0,
1647 VAR_MULTI_STOP_CMDS, DEF_MULTI_STOP_CMDS, &var_multi_stop_cmds, 0, 0,
1648 VAR_MULTI_CNTRL_CMDS, DEF_MULTI_CNTRL_CMDS, &var_multi_cntrl_cmds, 0, 0,
1649 0,
1650 };
1651 int instance_select_count = 0;
1652 int command_mode_count = 0;
1653 INST_SELECTION selection;
1654 NAME_ASSIGNMENT assignment;
1655 int iter_flags = ITER_FLAG_DEFAULT;
1656 int cmd_mode = 0;
1657 int code;
1658
1659 selection.type = INST_SEL_NONE;
1660 assignment.name = assignment.gname = 0;
1661
1662 /*
1663 * Fingerprint executables and core dumps.
1664 */
1665 MAIL_VERSION_STAMP_ALLOCATE;
1666
1667 /*
1668 * Be consistent with file permissions.
1669 */
1670 umask(022);
1671
1672 /*
1673 * To minimize confusion, make sure that the standard file descriptors
1674 * are open before opening anything else. XXX Work around for 44BSD where
1675 * fstat can return EBADF on an open file descriptor.
1676 */
1677 for (fd = 0; fd < 3; fd++)
1678 if (fstat(fd, &st) == -1
1679 && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
1680 msg_fatal("open /dev/null: %m");
1681
1682 /*
1683 * Set up diagnostics. XXX What if stdin is the system console during
1684 * boot time? It seems a bad idea to log startup errors to the console.
1685 * This is UNIX, a system that can run without hand holding.
1686 */
1687 if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
1688 argv[0] = slash + 1;
1689 if (isatty(STDERR_FILENO))
1690 msg_vstream_init(argv[0], VSTREAM_ERR);
1691 msg_syslog_init(argv[0], LOG_PID, LOG_FACILITY);
1692
1693 /*
1694 * Check the Postfix library version as soon as we enable logging.
1695 */
1696 MAIL_VERSION_CHECK;
1697
1698 if ((config_dir = getenv(CONF_ENV_PATH)) != 0
1699 && strcmp(config_dir, DEF_CONFIG_DIR) != 0)
1700 msg_fatal("Non-default configuration directory: %s=%s",
1701 CONF_ENV_PATH, config_dir);
1702
1703 /*
1704 * Parse switches.
1705 */
1706 while ((ch = GETOPT(argc, argv, "ae:g:i:G:I:lpRvx")) > 0) {
1707 switch (ch) {
1708 default:
1709 usage(argv[0]);
1710 /* NOTREACHED */
1711 case 'a':
1712 if (selection.type != INST_SEL_ALL)
1713 instance_select_count++;
1714 selection.type = INST_SEL_ALL;
1715 break;
1716 case 'e':
1717 if ((code = EDIT_CMD_CODE(optarg)) < 0)
1718 msg_fatal("Invalid '-e' edit action '%s'. Specify '%s', "
1719 "'%s', '%s', '%s', '%s', '%s', '%s' or '%s'",
1720 optarg,
1721 EDIT_CMD_STR(EDIT_CMD_CREATE),
1722 EDIT_CMD_STR(EDIT_CMD_DESTROY),
1723 EDIT_CMD_STR(EDIT_CMD_IMPORT),
1724 EDIT_CMD_STR(EDIT_CMD_DEPORT),
1725 EDIT_CMD_STR(EDIT_CMD_ENABLE),
1726 EDIT_CMD_STR(EDIT_CMD_DISABLE),
1727 EDIT_CMD_STR(EDIT_CMD_ASSIGN),
1728 EDIT_CMD_STR(EDIT_CMD_INIT));
1729 if (cmd_mode != code)
1730 command_mode_count++;
1731 cmd_mode = code;
1732 break;
1733 case 'g':
1734 instance_select_count++;
1735 selection.type = INST_SEL_GROUP;
1736 selection.name = optarg;
1737 break;
1738 case 'i':
1739 instance_select_count++;
1740 selection.type = INST_SEL_NAME;
1741 selection.name = optarg;
1742 break;
1743 case 'G':
1744 if (assignment.gname != 0)
1745 msg_fatal("Specify at most one '-G' option");
1746 assignment.gname = strcmp(optarg, "-") == 0 ? "" : optarg;
1747 break;
1748 case 'I':
1749 if (assignment.name != 0)
1750 msg_fatal("Specify at most one '-I' option");
1751 assignment.name = strcmp(optarg, "-") == 0 ? "" : optarg;
1752 break;
1753 case 'l':
1754 if (cmd_mode != ITER_CMD_LIST)
1755 command_mode_count++;
1756 cmd_mode = ITER_CMD_LIST;
1757 break;
1758 case 'p':
1759 if (cmd_mode != ITER_CMD_POSTFIX)
1760 command_mode_count++;
1761 cmd_mode = ITER_CMD_POSTFIX;
1762 break;
1763 case 'R':
1764 iter_flags ^= ITER_FLAG_REVERSE;
1765 break;
1766 case 'v':
1767 msg_verbose++;
1768 check_setenv(CONF_ENV_VERB, "");
1769 break;
1770 case 'x':
1771 if (cmd_mode != ITER_CMD_GENERIC)
1772 command_mode_count++;
1773 cmd_mode = ITER_CMD_GENERIC;
1774 break;
1775 }
1776 }
1777
1778 /*
1779 * Report missing arguments, or wrong arguments in the wrong context.
1780 */
1781 if (instance_select_count > 1)
1782 msg_fatal("Specity no more than one of '-a', '-g', '-i'");
1783
1784 if (command_mode_count != 1)
1785 msg_fatal("Specify exactly one of '-e', '-l', '-p', '-x'");
1786
1787 if (cmd_mode == ITER_CMD_LIST && argc > optind)
1788 msg_fatal("Command not allowed with '-l'");
1789
1790 if (cmd_mode == ITER_CMD_POSTFIX || cmd_mode == ITER_CMD_GENERIC)
1791 if (argc == optind)
1792 msg_fatal("Command required with '-p' or '-x' option");
1793
1794 if (cmd_mode == ITER_CMD_POSTFIX || (cmd_mode & EDIT_CMD_MASK_ALL))
1795 if (iter_flags != ITER_FLAG_DEFAULT)
1796 msg_fatal("The '-p' and '-e' options preclude the use of '-R'");
1797
1798 if ((cmd_mode & EDIT_CMD_MASK_ASSIGN) == 0
1799 && (assignment.name || assignment.gname)) {
1800 if ((cmd_mode & EDIT_CMD_MASK_ALL) == 0)
1801 msg_fatal("Cannot assign instance name or group without '-e %s'",
1802 EDIT_CMD_STR(EDIT_CMD_ASSIGN));
1803 else
1804 msg_fatal("Cannot assign instance name or group with '-e %s'",
1805 EDIT_CMD_STR(cmd_mode));
1806 }
1807 if (cmd_mode & EDIT_CMD_MASK_ALL) {
1808 if (cmd_mode == EDIT_CMD_ASSIGN
1809 && (assignment.name == 0 && assignment.gname == 0))
1810 msg_fatal("Specify new instance name or group with '-e %s'",
1811 EDIT_CMD_STR(cmd_mode));
1812
1813 if ((cmd_mode & ~EDIT_CMD_MASK_ADD) != 0 && argc > optind)
1814 msg_fatal("Parameter overrides not valid with '-e %s'",
1815 EDIT_CMD_STR(cmd_mode));
1816 }
1817
1818 /*
1819 * Proces main.cf parameters.
1820 */
1821 mail_conf_read();
1822 get_mail_conf_str_table(str_table);
1823
1824 /*
1825 * Sanity checks.
1826 */
1827 check_shared_dir_status();
1828
1829 /*
1830 * Iterate over selected instances, or manipulate one instance.
1831 */
1832 if (cmd_mode & ITER_CMD_MASK_ALL)
1833 iterate(cmd_mode, iter_flags, argc - optind, argv + optind, &selection);
1834 else
1835 manage(cmd_mode, argc - optind, argv + optind, &selection, &assignment);
1836}
1837